package beetle

import (
	"beetle/app"
	"beetle/routers"
	"context"
	"fmt"
	"net/http"
	"reflect"
	"time"
)

func Run(httpPort string, timeOut time.Duration) {
	Service.BindingControllerRouterMapping(routers.Routers)
	//注册组件,例如redis mysql
	Service.InitComponents()
	//注册中间件
	Service.RunWithMiddleWares()
	//地址:127.0.0.1:8000
	Service.RunHttp(httpPort, timeOut)
}

type service interface {
	BindingControllerRouterMapping(routers map[string]func(http.ResponseWriter, *http.Request))
	RunWithMiddleWares()
	InitComponents()
	RunHttp(httpPort string, timeOut time.Duration)
}
type middleWares interface {
	BeforeRunControllers(response http.ResponseWriter, input *http.Request)
}
type httpService struct {
	routers map[string]func(http.ResponseWriter, *http.Request)
}

var Service service = &httpService{}

func (this *httpService) BindingControllerRouterMapping(routers map[string]func(http.ResponseWriter, *http.Request)) {
	this.routers = routers
}

var middleware func(res http.ResponseWriter, input *http.Request)

func (this *httpService) RunWithMiddleWares() {
	var middleWares middleWares = app.Guard{}
	middleware = middleWares.BeforeRunControllers
}

//通过反射注册组件
func (this *httpService) InitComponents() {
	var factory app.AppFactory
	components := reflect.TypeOf(factory)
	for i := 0; i < components.NumMethod(); i++ {
		method := components.Method(i)
		method.Func.Call([]reflect.Value{reflect.ValueOf(factory)})
	}
}

func (this *httpService) RunHttp(httpPort string, timeOut time.Duration) {
	for router, _ := range this.routers {
		newRouter := router
		http.HandleFunc(newRouter, func(w http.ResponseWriter, r *http.Request) {
			ctx, cancel := context.WithTimeout(context.Background(), timeOut*time.Millisecond)
			defer cancel()
			middleware(w, r)
			workDone := make(chan struct{}, 1)
			go func() {
				this.routers[newRouter](w, r)
				workDone <- struct{}{}
			}()
			//监听超时,回收goroutine
			select { //下面的case只执行最早到来的那一个
			case <-workDone:
			case <-ctx.Done(): //ctx.Done()是一个管道，context超时或者调用了cancel()都会关闭这个管道，然后读操作就会立即返回
				fmt.Println("LongTimeWork timeout")
			}
		})
	}
	fmt.Println("runing 127.0.0.1:" + httpPort)
	http.ListenAndServe("127.0.0.1:"+httpPort, nil)
}
